#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>

#include "head.h"

#define CNAME "hello"
int major = 0;
unsigned int *red_base = NULL;
unsigned int *green_base = NULL;
unsigned int *blue_base = NULL;
char kbuf[128] = {0};
struct class *cls;
struct device *dev;
int led_open(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);

	*(red_base + 9) &= ~(3 << 24); //gpio mode
	*(red_base + 1) |= (1 << 28);  //output
	*(red_base) &= ~(1 << 28);	 //low level

	*(green_base + 8) &= ~(3 << 26);
	*(green_base + 1) |= (1 << 13);
	*(green_base) &= ~(1 << 13);

	*(blue_base + 8) &= ~(3 << 24);
	*(blue_base + 8) |= (2 << 24);
	*(blue_base + 1) |= (1 << 12);
	*(blue_base) &= ~(1 << 12);

	return 0;
}
ssize_t led_read(struct file *file, char __user *ubuf, size_t size, loff_t *offs)
{
	int ret;
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	if (size > sizeof(kbuf))
		size = sizeof(kbuf);
	ret = copy_to_user(ubuf, kbuf, size);
	if (ret)
	{
		printk("copy data to user error\n");
		return -EIO;
	}

	return size;
}

ssize_t led_write(struct file *file, const char __user *ubuf, size_t size, loff_t *offs)
{
	int ret;
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	if (size > sizeof(kbuf))
		size = sizeof(kbuf);
	ret = copy_from_user(kbuf, ubuf, size);
	if (ret)
	{
		printk("copy data from user error\n");
		return -EIO;
	}

	return size;
}
long led_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
	int ret;
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	switch (cmd)
	{
	case RED_ON:
		*(red_base) |= (1 << 28);
		break;
	case RED_OFF:
		*(red_base) &= ~(1 << 28); //low level
		break;
	case GREEN_ON:
		*(green_base) |= (1 << 13);
		break;
	case GREEN_OFF:
		*(green_base) &= ~(1 << 13); //low level
		break;
	case BLUE_ON:
		*(blue_base) |= (1 << 12);
		break;
	case BLUE_OFF:
		*(blue_base) &= ~(1 << 12);
		; //low level
		break;
	case ACCESS_DATA_W:
		ret = copy_from_user(kbuf, (void *)args, sizeof(kbuf));
		if (ret)
		{
			printk("copy data from user error\n");
			return -EIO;
		}
		printk("kbuf = %s\n", kbuf);
		break;
	}

	return 0;
}

int led_close(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	return 0;
}

const struct file_operations fops = {
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.unlocked_ioctl = led_ioctl,
	.release = led_close,
};

//闁稿繈鍎辫ぐ锟�
static int __init demo_init(void)
{
	int i;
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	major = register_chrdev(0, CNAME, &fops);
	if (major <= 0)
	{
		printk("register char devices driver error\n");
		return -EAGAIN;
	}
	printk("major = %d\n", major);

	//閻忓繐鏀眅d闁汇劌瀚﹢鎾锤閳ь剟寮伴悩鑼闁告垹鍎ゅ锟�
	red_base = ioremap(0xc001a000, 40);
	if (red_base == NULL)
	{
		printk("ioremap red error\n");
		return -ENOMEM;
	}
	green_base = ioremap(0xc001e000, 40);
	if (green_base == NULL)
	{
		printk("ioremap green error\n");
		return -ENOMEM;
	}
	blue_base = ioremap(0xc001b000, 40);
	if (blue_base == NULL)
	{
		printk("ioremap blue error\n");
		return -ENOMEM;
	}
	//閸掓稑缂揷lass
	cls = class_create(THIS_MODULE, "class-hello");
	if (IS_ERR(cls))
	{
		printk("class create error\n");
		return PTR_ERR(cls);
	}
	//閸掓稑缂揹evice
	for (i = 0; i < 3; i++)
	{
		dev = device_create(cls, NULL, MKDEV(major, i),
							NULL, "dev-led%d", i);
	}
	return 0;
}
//闁告垵鎼ぐ锟�
static void __exit demo_exit(void)
{
	int i;
	for (i = 0; i < 3; i++)
	{
		device_destroy(cls, MKDEV(major, i));
	}
	class_destroy(cls);
	iounmap(red_base);
	iounmap(green_base);
	iounmap(blue_base);
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	unregister_chrdev(major, CNAME);
}

module_init(demo_init);
module_exit(demo_exit);
//閻犱胶枪瑜拌尙鎷犻敓锟�
MODULE_LICENSE("GPL");
